using Publicuse.Entity;
using Publicuse.Entity.AttributeUtil;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Publicuse.Util
{
    /// <summary>
    /// 查询条件组合辅助类
    /// </summary>
    public static class QueryCommonUtil
    {
        /// <summary>
        /// 获取查询条件组合返回SQL字符串
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="func"></param>
        /// <param name="sql"></param>
        /// <returns></returns>
        public static string GetQueryString<T>(this T query, Func<string, string> func, string sql = " where 1=1 ") where T : new()
        {
            var qdata = query.GetType().GetPropertiesWithNoQuery();
            foreach (var item in qdata)
            {
                var itemvalue = item?.GetValue(query)?.ToString();
                if (!string.IsNullOrEmpty(itemvalue) && string.Compare(item.Name, "status", true) != 0
                && string.Compare(item.Name, "cTime", true) != 0 && string.Compare(item.Name, "uTime", true) != 0
                && string.Compare(item.Name, "Id", true) != 0 && string.Compare(itemvalue, "-1", true) != 0 && string.Compare(itemvalue, "1811-11-11 11:11:11", true) != 0
                && string.Compare(itemvalue, "0001-01-01 0:00:00", true) != 0)
                {

                    if (string.Compare(item.PropertyType.FullName, "System.String", true) == 0)
                    {
                        var protype = item.GetCustomAttributes(typeof(EqueryQueryAttribute), true);
                        if (string.IsNullOrEmpty(itemvalue) || string.Compare("null", itemvalue, true) == 0)
                        {
                            continue;
                        }
                        if (protype.Count() > 0)
                        {
                            sql += $" and {item.Name} = '{itemvalue.Trim()}'";
                        }
                        else
                        {
                            sql += $" and {item.Name} like '%{itemvalue.Trim()}%'";
                        }
                    }
                    else if (string.Compare(item.PropertyType.FullName, "System.Int32", true) == 0 ||
                        string.Compare(item.PropertyType.FullName, "System.Int64", true) == 0 ||
                        string.Compare(item.PropertyType.FullName, "System.Float", true) == 0)
                    {
                        sql += $" and {item.Name} = {itemvalue.Trim()}";
                    }
                }
                if (string.Compare(item.Name, "status", true) == 0)
                {
                    if (itemvalue != "-1" && itemvalue != "-9")
                    {
                        sql += $" and status ={itemvalue} ";
                    }
                    if (itemvalue == "-1")
                    {
                        sql += " and status<>-9 ";
                    }
                    if (itemvalue == "-9")
                    {
                        sql += $" and status ={itemvalue} ";
                    }
                }
            }

            qdata = query.GetType().GetPropertiesWithCombinationQuery();
            foreach (var item in qdata)
            {
                var itemvalue = item.GetValue(query)?.ToString();
                if (!string.IsNullOrEmpty(itemvalue) && string.Compare(item.Name, "status", true) != 0
                && string.Compare(item.Name, "cTime", true) != 0 && string.Compare(item.Name, "uTime", true) != 0
                && string.Compare(item.Name, "Id", true) != 0 && string.Compare(itemvalue, "-1", true) != 0 && string.Compare(itemvalue, "1811-11-11 11:11:11", true) != 0
                && string.Compare(itemvalue, "0001-01-01 0:00:00", true) != 0)
                {
                    if (string.Compare(item.PropertyType.FullName, "System.String", true) == 0)
                    {
                        sql += $" and {item.Name} like '%{itemvalue.Trim()}%'";
                    }
                    else if (string.Compare(item.PropertyType.FullName, "System.Int32", true) == 0 ||
                        string.Compare(item.PropertyType.FullName, "System.Int64", true) == 0 ||
                        string.Compare(item.PropertyType.FullName, "System.Float", true) == 0)
                    {
                        sql += $" and {item.Name} = {itemvalue.Trim()}";
                    }
                }
            }

            qdata = query.GetType().GetPropertiesIdsQuery();
            foreach (var item in qdata)
            {
                var exportids = item.GetCustomAttributes(typeof(ExportidsAttribute), true);
                if (exportids.Count() > 0)
                {
                    var itemvalue = item.GetValue(query)?.ToString();
                    if (!string.IsNullOrEmpty(itemvalue))
                    {
                        sql += $" and Id in({itemvalue.Trim()})";
                    }
                }
            }

            if (func != null)
            {
                sql = func.Invoke(sql);
            }
            return sql;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="func"></param>
        /// <param name="sql"></param>
        /// <returns></returns>
        public static string GetQueryStringAndParameter<T>(this T query, Func<string, List<SugarParameter>> func, out List<SugarParameter> parameters,out string msg,string sql =" where 1=1 ")
        {
            msg = string.Empty;
            parameters = new List<SugarParameter>();
            try
            {
                var qdata = query.GetType().GetPropertiesWithNoQuery();
                foreach (var item in qdata)
                {
                    var itemvalue = item?.GetValue(query)?.ToString();
                    if (!string.IsNullOrEmpty(itemvalue) && string.Compare(item.Name, "status", true) != 0
                    && string.Compare(item.Name, "cTime", true) != 0 && string.Compare(item.Name, "uTime", true) != 0
                    && string.Compare(item.Name, "Id", true) != 0 && string.Compare(itemvalue, "-1", true) != 0 && string.Compare(itemvalue, "1811-11-11 11:11:11", true) != 0
                    && string.Compare(itemvalue, "0001-01-01 0:00:00", true) != 0)
                    {

                        if (string.Compare(item.PropertyType.FullName, "System.String", true) == 0)
                        {
                            var protype = item.GetCustomAttributes(typeof(EqueryQueryAttribute), true);
                            if (string.IsNullOrEmpty(itemvalue) || string.Compare("null", itemvalue, true) == 0)
                            {
                                continue;
                            }
                            if (protype.Count() > 0)
                            {
                                sql += $" and {item.Name} = @{item.Name}";
                                parameters.Add(new SugarParameter($"@{item.Name}", $"{itemvalue}"));
                            }
                            else
                            {
                                sql += $" and {item.Name} like @{item.Name}";
                                parameters.Add(new SugarParameter($"@{item.Name}", $"%{itemvalue}%"));
                            }
                        }
                        else if (string.Compare(item.PropertyType.FullName, "System.Int32", true) == 0 ||
                            string.Compare(item.PropertyType.FullName, "System.Int64", true) == 0 ||
                            string.Compare(item.PropertyType.FullName, "System.Float", true) == 0)
                        {
                            sql += $" and {item.Name} = @{item.Name}";
                            parameters.Add(new SugarParameter($"@{item.Name}", $"{itemvalue}"));
                        }
                    }
                    if (string.Compare(item.Name, "status", true) == 0)
                    {
                        if (itemvalue != "-1" && itemvalue != "-9")
                        {
                            sql += $" and status ={itemvalue} ";
                        }
                        if (itemvalue == "-1")
                        {
                            sql += " and status<>-9 ";
                        }
                        if (itemvalue == "-9")
                        {
                            sql += $" and status ={itemvalue} ";
                        }
                    }
                }
                if (func != null)
                {
                    parameters = func.Invoke(sql);
                }
            }
            catch (Exception ex)
            {
                msg = ex.Message;
            }
            return sql;
        }

        /// <summary>
        /// 获取查询条件组合返回表达树目录式
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="query"></param>
        /// <param name="func"></param>
        /// <param name="msg"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> GetQueryExpression<T>(this T query, Func<Expression<Func<T, bool>>, Expression<Func<T, bool>>> func, out string msg)
        {
            msg = string.Empty;
            Expression<Func<T, bool>> expression = null;
            try
            {
                var qdata = query.GetType().GetPropertiesWithNoQuery();
                foreach (var item in qdata)
                {
                    var itemvalue = item?.GetValue(query)?.ToString();
                    if (!string.IsNullOrEmpty(itemvalue) && string.Compare(item.Name, "status", true) != 0
                    && string.Compare(item.Name, "cTime", true) != 0 && string.Compare(item.Name, "uTime", true) != 0
                    && string.Compare(item.Name, "Id", true) != 0 && string.Compare(itemvalue, "-1", true) != 0 && string.Compare(itemvalue, "1811-11-11 11:11:11", true) != 0
                    && string.Compare(itemvalue, "0001-01-01 0:00:00", true) != 0)
                    {

                        if (string.Compare(item.PropertyType.FullName, "System.String", true) == 0)
                        {
                            var protype = item.GetCustomAttributes(typeof(EqueryQueryAttribute), true);
                            if (string.IsNullOrEmpty(itemvalue) || string.Compare("null", itemvalue, true) == 0)
                            {
                                continue;
                            }
                            if (protype.Count() > 0)
                            {
                                ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "a");
                                ConstantExpression constantrichard = Expression.Constant(itemvalue.Trim(), typeof(string));
                                PropertyInfo propName = typeof(T).GetProperty(item.Name);
                                Expression nameExp = Expression.Property(parameterExpression, propName);
                                MethodInfo equals = typeof(string).GetMethod("Equals", new Type[] { typeof(string) });
                                Expression NameExp = Expression.Call(nameExp, equals, constantrichard);
                                Expression<Func<T, bool>> predicate1 = Expression.Lambda<Func<T, bool>>(NameExp, new ParameterExpression[1]
                                {
                                   parameterExpression
                                });
                                expression = expression.And(predicate1);
                            }
                            else
                            {
                                ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "a");
                                ConstantExpression constantrichard = Expression.Constant(itemvalue.Trim(), typeof(string));
                                PropertyInfo propName = typeof(T).GetProperty(item.Name);
                                Expression nameExp = Expression.Property(parameterExpression, propName);
                                MethodInfo Contains = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
                                Expression NameExp = Expression.Call(nameExp, Contains, constantrichard);
                                Expression<Func<T, bool>> predicate1 = Expression.Lambda<Func<T, bool>>(NameExp, new ParameterExpression[1]
                                {
                                   parameterExpression
                                });
                                expression = expression.And(predicate1);
                            }
                        }
                        else if (string.Compare(item.PropertyType.FullName, "System.Int32", true) == 0 ||
                            string.Compare(item.PropertyType.FullName, "System.Int64", true) == 0 ||
                            string.Compare(item.PropertyType.FullName, "System.Float", true) == 0)
                        {
                            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "a");
                            ConstantExpression constantrichard = Expression.Constant(itemvalue.Trim().GetIntValue(), typeof(int));
                            PropertyInfo propName = typeof(T).GetProperty(item.Name);
                            Expression nameExp = Expression.Property(parameterExpression, propName);
                            MethodInfo equals = typeof(int).GetMethod("Equals", new Type[] { typeof(int) });
                            Expression NameExp = Expression.Call(nameExp, equals, constantrichard);
                            Expression<Func<T, bool>> predicate1 = Expression.Lambda<Func<T, bool>>(NameExp, new ParameterExpression[1]
                            {
                                   parameterExpression
                            });
                            expression = expression.And(predicate1);
                        }
                    }
                    if (string.Compare(item.Name, "status", true) == 0)
                    {
                        if ((itemvalue != "-1" && itemvalue != "-9") || itemvalue == "-9")
                        {
                            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "a");
                            ConstantExpression constant = Expression.Constant(itemvalue.GetIntValue(), typeof(int));
                            PropertyInfo prop = typeof(T).GetProperty("status");
                            Expression Exp = Expression.Property(parameterExpression, prop);
                            Expression expressionExp = Expression.Equal(Exp, constant);
                            Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(expressionExp, new ParameterExpression[1]
                            {
                               parameterExpression
                            });
                            expression = expression.And(predicate);
                        }
                        if (itemvalue == "-1")
                        {
                            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "a");
                            ConstantExpression constant = Expression.Constant(-9, typeof(int));
                            PropertyInfo prop = typeof(T).GetProperty("status");
                            Expression Exp = Expression.Property(parameterExpression, prop);
                            Expression expressionExp = Expression.NotEqual(Exp, constant);
                            Expression<Func<T, bool>> predicate = Expression.Lambda<Func<T, bool>>(expressionExp, new ParameterExpression[1]
                            {
                               parameterExpression
                            });
                            expression = expression.And(predicate);
                        }
                    }
                }

                qdata = query.GetType().GetPropertiesWithCombinationQuery();
                foreach (var item in qdata)
                {
                    var itemvalue = item.GetValue(query)?.ToString();
                    if (!string.IsNullOrEmpty(itemvalue) && string.Compare(item.Name, "status", true) != 0
                    && string.Compare(item.Name, "cTime", true) != 0 && string.Compare(item.Name, "uTime", true) != 0
                    && string.Compare(item.Name, "Id", true) != 0 && string.Compare(itemvalue, "-1", true) != 0 && string.Compare(itemvalue, "1811-11-11 11:11:11", true) != 0
                    && string.Compare(itemvalue, "0001-01-01 0:00:00", true) != 0)
                    {
                        if (string.Compare(item.PropertyType.FullName, "System.String", true) == 0)
                        {
                            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "a");
                            ConstantExpression constantrichard = Expression.Constant(itemvalue.Trim(), typeof(string));
                            PropertyInfo propName = typeof(T).GetProperty(item.Name);
                            Expression nameExp = Expression.Property(parameterExpression, propName);
                            MethodInfo Contains = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
                            Expression NameExp = Expression.Call(nameExp, Contains, constantrichard);
                            Expression<Func<T, bool>> predicate1 = Expression.Lambda<Func<T, bool>>(NameExp, new ParameterExpression[1]
                            {
                                   parameterExpression
                            });
                            expression = expression.And(predicate1);
                        }
                        else if (string.Compare(item.PropertyType.FullName, "System.Int32", true) == 0 ||
                            string.Compare(item.PropertyType.FullName, "System.Int64", true) == 0 ||
                            string.Compare(item.PropertyType.FullName, "System.Float", true) == 0)
                        {
                            ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "a");
                            ConstantExpression constantrichard = Expression.Constant(itemvalue.Trim().GetIntValue(), typeof(int));
                            PropertyInfo propName = typeof(T).GetProperty(item.Name);
                            Expression nameExp = Expression.Property(parameterExpression, propName);
                            MethodInfo equals = typeof(int).GetMethod("Equals", new Type[] { typeof(int) });
                            Expression NameExp = Expression.Call(nameExp, equals, constantrichard);
                            Expression<Func<T, bool>> predicate1 = Expression.Lambda<Func<T, bool>>(NameExp, new ParameterExpression[1]
                            {
                                   parameterExpression
                            });
                            expression = expression.And(predicate1);
                        }
                    }
                }

                qdata = query.GetType().GetPropertiesIdsQuery();
                foreach (var item in qdata)
                {
                    var exportids = item.GetCustomAttributes(typeof(ExportidsAttribute), true);
                    if (exportids.Count() > 0)
                    {
                        var itemvalue = item.GetValue(query)?.ToString();
                        if (!string.IsNullOrEmpty(itemvalue))
                        {
                            expression = BuildInExpression<T>(p => p.Id.ToString(), itemvalue.Trim().Split(','));
                        }
                    }
                }

                if (func != null)
                {
                    expression = func?.Invoke(expression);
                }
            }
            catch (Exception ex)
            {
                msg = ex.Message;
            }
            return expression;
        }

        /// <summary>
        /// 去除字符串空格返回新的实体信息
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static T GetNewInfo<T>(this T entity) where T : new()
        {
            var adata = entity.GetType().GetPropertiesWithNoInsert();
            foreach (var item in adata)
            {
                if (string.Compare(item.PropertyType.FullName, "System.String", true) == 0)
                {
                    item.SetValue(entity, item.GetValue(entity)?.ToString().Trim());
                }
            }
            return entity;
        }

        // 构建表达式树的Helper方法
        public static Expression<Func<T, bool>> BuildInExpression<T>(Expression<Func<BaseModel<long>, string>> propertyExpression, IEnumerable<string> values)
        {
            var parameter = propertyExpression.Parameters.Single();
            var property = propertyExpression.Body as MemberExpression;

            var equals = values.Select(value => Expression.Equal(property, Expression.Constant(value))).Aggregate<Expression>(Expression.OrElse);

            return Expression.Lambda<Func<T, bool>>(equals, parameter);
        }


        // 构建表达式树的Helper方法
        public static Expression<Func<T, bool>> BuildInExpressionb<T>(Expression<Func<T, string>> propertyExpression, IEnumerable<string> values)
        {
            var parameter = propertyExpression.Parameters.Single();
            var property = propertyExpression.Body as MemberExpression;

            var equals = values.Select(value => Expression.Equal(property, Expression.Constant(value))).Aggregate<Expression>(Expression.OrElse);

            return Expression.Lambda<Func<T, bool>>(equals, parameter);
        }
    }
}
